import { filterFont, FontType } from './font';

export type StyleResetContext = {
  // 根节点字体
  rootFont?: string | string[],
  // 根节点字体磅重
  rootWeight: number,
  // ui 字体，针对 input, select, button
  uiFont?: string | string[],
  // ui 磅重
  uiWeight: number,
  // 等宽字体
  monoFont?: string | string[],
  // 等宽字体磅重
  monoWeight: number,
  // 粗体字体（heading, bold）
  boldFont?: string | string[],
  // 粗体字体磅重
  boldWeight?: number,
  // 文本描边宽度
  textStrokeWidth?: string,
  // 粗体描边宽度
  boldStrokeWidth?: string,
}

// const opCache: Record<string, string> = {};

const getFontTypeByKey = (key: keyof StyleResetContext | string): FontType => {
  if (key === 'monoFont') return FontType.monospace;
  return FontType.default;
};

const defaultContext = (): StyleResetContext => ({
  rootWeight     : 400,
  uiWeight       : 400,
  monoWeight     : 300,
  boldWeight     : 700,
  textStrokeWidth: '0.010em',
  boldStrokeWidth: '0.025em',
});

// const prepareValue = (key: keyof StyleResetContext | string, value: unknown): string => {
//   if (opCache[key] == null) {
//     if (/font/i.test(key)) {
//       opCache[key] = filterFont(value, getFontTypeByKey(key));
//     } else {
//       opCache[key] = value + '';
//     }
//   }
//   return opCache[key];
// };

// export const buildCSS = (tpl: string, ctx: StyleResetContext): string => {
//   return tpl.replace(/\${([^{}]+)}/gm, (_raw: string, key: string): string => {
//     // @ts-ignore
//     return prepareValue(key, ctx[key] || null);
//   });
// };

export const css = (value: TemplateStringsArray): string => value.raw.join('').trim();

export enum AppendMode {
  head,
  tail
}

export class CSSBuilder {

  // context: StyleResetContext;
  //
  // buffer?: string[];

  constructor(
    context: Partial<StyleResetContext>,
    css?: string | string[] | null,
  ) {
    Object.defineProperties(this, {
      _context: {
        value     : defaultContext(),
        enumerable: false,
      },
      _buffer : {
        value     : [],
        enumerable: false,
        writable  : true,
      },
    });
    this.context = context;
    this.appendCSS(css);
  }

  // 这里之所以这么麻烦，因为 vite library 模式不能输出低版本的 ES 代码
  // 如果使用 class.prop 方式声明，会导致编译时，会额外加入了一些全局代码
  get context(): StyleResetContext {
    // @ts-ignore
    return this._context;
  }

  set context(ctx: Partial<StyleResetContext>) {
    // @ts-ignore
    Object.assign(this._context, ctx);
  }

  get buffer(): string[] {
    // @ts-ignore
    return this._buffer;
  }

  set buffer(buffer: string[]) {
    // @ts-ignore
    this._buffer = buffer;
  }

  purgeCSS(css: string): string {
    return css.trim();
  }

  appendCSS(css?: string | string[] | null, mode = AppendMode.tail): this {
    if (css == null) {
      return this;
    }
    if (!Array.isArray(css)) {
      css = [css];
    }
    const filtered = css.map(this.purgeCSS).filter(Boolean);
    if (filtered.length <= 0) {
      return this;
    }
    if (mode === AppendMode.head) {
      this.buffer = filtered.concat(...this.buffer);
    } else {
      this.buffer = this.buffer.concat(...filtered);
    }
    return this;
  }

  getContextValue(key: string): string {
    // @ts-ignore
    const value = this.context[key] || null;
    if (/font/i.test(key)) {
      if (value == null) {
        return 'inherit';
      }
      return filterFont(value, getFontTypeByKey(key));
    } else {
      return value + '';
    }
  }

  build(): string {
    if (this.buffer.length <= 0) {
      throw new Error('CSS 内容为空');
    }
    return this.buffer.join('\n').replace(/\[\[([^\[\]]+)\]\]/gm, (_raw: string, key: string): string => {
      // @ts-ignore
      return this.getContextValue(key);
    }).trim();
  }
}